package mweb

import (
	"compress/gzip"
	"fmt"
	"gitee.com/dennis-mxx/mxx-core/mexception"
	"gitee.com/dennis-mxx/mxx-core/mlogger"
	"gitee.com/dennis-mxx/mxx-core/mutil"
	"github.com/go-playground/validator"
	"github.com/kataras/iris/v12/context"
	"github.com/kataras/iris/v12/sessions"
	"mime/multipart"
	"reflect"
	"strings"
)

type RequestContext struct {
	Application *irisWebApplication
	Ctx         *context.Context
	Route       Route
	Method      string
	ContentType string
	Url         string
	UserAgent   string
	Validate    *validator.Validate
	sess        *sessions.Sessions
}

func (domain *RequestContext) GetIp() string {
	xForwardedFor := domain.Ctx.GetHeader("x-forwarded-for")
	remoteAddr := domain.Ctx.RemoteAddr()
	host := domain.Ctx.Host()
	return mutil.IP.GetRealIP(xForwardedFor, remoteAddr, host)
}
func (domain *RequestContext) StatusCode(code int) *RequestContext {
	domain.Ctx.StatusCode(code)
	return domain
}
func (domain *RequestContext) Session() *sessions.Session {
	return domain.sess.Start(domain.Ctx)
}
func (domain *RequestContext) GetCookie(name string) (cookieValue string) {
	return domain.Ctx.GetCookie(name)
}
func (domain *RequestContext) SetCookie(name string, value string) {
	domain.Ctx.SetCookieKV(name, value)
}
func (domain *RequestContext) RemoveCookie(name string) {
	domain.Ctx.RemoveCookie(name)
}
func (domain *RequestContext) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
	return domain.Ctx.FormFile(key)
}
func (domain *RequestContext) Next() {
	domain.Ctx.Next()
}

func (domain *RequestContext) ScanParams(dest interface{}) {
	var err error
	if domain.Method == "POST" {
		if strings.Contains(domain.ContentType, "json") {
			err = domain.Ctx.ReadJSON(dest)
		} else if strings.Contains(domain.ContentType, "form") {
			err = domain.Ctx.ReadForm(dest)
		} else if strings.Contains(domain.ContentType, "xml") {
			err = domain.Ctx.ReadXML(dest)
		} else {
			err = domain.Ctx.ReadForm(dest)
		}
		if err != nil {
			panic(mexception.NewError(err))
		} else if domain.Route.ParamValidate {
			if err = domain.ValidateParams(dest); err != nil {
				panic(err)
			}
		}
	} else {
		panic(mexception.NewException(mexception.UnsupportedException, fmt.Sprintf("unsupported request method:[%s]", domain.Method)))
	}
}
func (domain *RequestContext) ScanUrlParams(dest interface{}) {
	params := domain.Ctx.URLParams()
	var err error
	if err = mutil.Json.Clone(params, dest); err == nil {
		if domain.Route.ParamValidate {
			err = domain.ValidateParams(dest)
		}
	}
	if err != nil {
		panic(err)
	}
}

func (domain *RequestContext) ValidateParams(dest interface{}) error {
	err := domain.Validate.Struct(dest)
	if err == nil {
		return nil
	}
	if reflect.TypeOf(err).String() == "validator.ValidationErrors" {
		validationErrors := err.(validator.ValidationErrors)

		if validationErrors != nil {
			buff := strings.Builder{}
			for i := 0; i < len(validationErrors); i++ {
				fieldError := validationErrors[i]
				field := fieldError.Field()
				buff.WriteString("validation [")
				buff.WriteString(field)
				buff.WriteString("] failed on the ")
				buff.WriteString(fieldError.Tag())
				if i < len(validationErrors)-1 {
					buff.WriteString(" ; ")
				}
			}
			return mexception.NewException(mexception.ValidationException, buff.String())
		}
	} else {
		return mexception.NewError(err)
	}
	return nil
}

func (domain *RequestContext) WriteJSON(dest interface{}) {
	if err := domain.Ctx.JSON(dest); err != nil {
		panic(mexception.NewError(err))
	}
}
func (domain *RequestContext) WriteText(dest string) {
	if _, err := domain.Ctx.Text(dest); err != nil {
		panic(mexception.NewError(err))
	}
}

func (domain *RequestContext) WriteGzip(fileName string, suffix string, data []byte) {
	domain.Ctx.Header("Content-Disposition", "attachment;filename="+fileName)
	domain.Ctx.ContentType(fmt.Sprintf("%s; charset=UTF-8", mutil.GetExtension(suffix)))
	domain.Ctx.Header("Pragma", "No-cache")
	domain.Ctx.Header("Cache-Control", "no-cache")
	domain.Ctx.Header("Expire", "0")

	if size, err := gzip.NewWriter(domain.Ctx.ResponseWriter()).Write(data); err != nil {
		panic(mexception.NewError(err))
	} else {
		domain.Ctx.Header("Content-length", fmt.Sprintf("%v", size))
	}

}
func (domain *RequestContext) WriteFile(suffix string, data []byte) {
	domain.Ctx.ContentType(fmt.Sprintf("%s; charset=UTF-8", mutil.GetExtension(suffix)))
	domain.Ctx.Header("Pragma", "No-cache")
	domain.Ctx.Header("Cache-Control", "No-cache")
	domain.Ctx.Header("Expire", "0")
	if size, err := domain.Ctx.Write(data); err != nil {
		panic(mexception.NewError(err))
	} else {
		domain.Ctx.Header("Content-length", fmt.Sprintf("%v", size))
	}
}

func (domain *RequestContext) Println() {
	mlogger.Logger.Debug(fmt.Sprintf("webReuqestContext: {ip:%s,url:%s,method:%s,userAgent:%s}", domain.GetIp(), domain.Url, domain.Method, domain.UserAgent))
}
